#include "SerialConnection.h"


#include "WinException.h"
#include "BipBuffer.h"

using namespace std;


SerialConnection::SerialConnection() 
	: hComm(INVALID_HANDLE_VALUE)
	, mOverlappedRead() // initialize the struct to 0
	, mOverlappedWrite()  // initialize the struct to 0
{}

std::string SerialConnection::toString() {
	return "SerialConnection";
}


// throws WinException
void SerialConnection::close() {
	// cause ReadFile() to return
	if (!::CloseHandle(hComm)) 
		throw WinException("SerialConnection::close() : CloseHandle()",GetLastError());
	hComm = INVALID_HANDLE_VALUE;
}


// throws WinException
int SerialConnection::read(char* buf, int bufSize) {
	DWORD bytesRead;
	
	if (::ReadFile( hComm, buf, bufSize , &bytesRead, &mOverlappedRead)) {
		// read returned immediately
	} else {
		switch(GetLastError()) {
		case ERROR_IO_PENDING: 
			// wait for the read to complete
			if (!::GetOverlappedResult(hComm, &mOverlappedRead, &bytesRead, TRUE)) 
				throw WinException(__FUNCTION__ " GetOverlappedResult()", GetLastError());
			break;

		default:
			throw WinException(__FUNCTION__ " ReadFile()", GetLastError());
		}
	}

	return bytesRead;
}

// throws WinException
void SerialConnection::sendString(string message) {	
	printf(__FUNCTION__ " message: %s length %u\n", message.c_str(), message.length());
	DWORD bytesWritten;

	if (::WriteFile(hComm, message.c_str(), message.length(), &bytesWritten, &mOverlappedWrite)) {
		printf(__FUNCTION__ "1 written: %u\n", bytesWritten);
		// write completed immediately
	} else {
		// either the write failed or it is now completing asynchronously
		switch(GetLastError()) {
		case ERROR_IO_PENDING: 
			// wait for the write to complete
			if (!::GetOverlappedResult(hComm, &mOverlappedWrite, &bytesWritten, TRUE)) 
				throw WinException(__FUNCTION__ " GetOverlappedResult()", GetLastError());
			printf(__FUNCTION__ "2 written: %u\n", bytesWritten);
			break;

		default: 
			throw WinException(__FUNCTION__ " WriteFile()", GetLastError());
		}
	}
}


// throws WinException
void SerialConnection::connect(string comport) {

	 hComm = ::CreateFileA( comport.c_str(),  
						GENERIC_READ | GENERIC_WRITE, 
						0, 
						0, 
						OPEN_EXISTING,	
						FILE_FLAG_OVERLAPPED, // 0,
						0);
	
	if (hComm == INVALID_HANDLE_VALUE) throw WinException( __FUNCTION__ " CreateFile()", GetLastError());
	
	// create an event for asynchronous (overlapped) reads
	mOverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (mOverlappedRead.hEvent == NULL) throw WinException(__FUNCTION__  " CreateEvent()", GetLastError());
	
	// create an event for asynchronous (overlapped) writes
	mOverlappedWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if (mOverlappedWrite.hEvent == NULL) throw WinException(__FUNCTION__  " CreateEvent()", GetLastError());

	// try to complete any outstanding operations
	if (!::FlushFileBuffers(hComm)) throw WinException(__FUNCTION__ " FlushFileBuffers()", GetLastError());

	// clear any bufferes (deletes all unsent data)
	if (!::PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) throw WinException(__FUNCTION__ " PurgeComm()", GetLastError()); 


	COMSTAT     comStat;
	DWORD       dwErrorFlags;
	if (!::ClearCommError( hComm, &dwErrorFlags, &comStat )) throw WinException(__FUNCTION__ " ClearCommError()", GetLastError());


	DWORD mask;
	if (!::GetCommMask(hComm, &mask)) throw WinException(__FUNCTION__ " GetCommMask()", GetLastError());
	mask |= EV_RXCHAR | EV_BREAK;
	printf("mask: %u\n",mask);
	if (!::SetCommMask( hComm, mask)) throw WinException(__FUNCTION__ " GetCommMask()", GetLastError());
	
	/* initialize the COM port */

	// read and modify the port settings
	DCB dcb = {0};
	if (!GetCommState(hComm, &dcb)) throw WinException(__FUNCTION__ " GetCommState()", GetLastError());

   

	////RVM
	dcb.BaudRate = 9600;
	dcb.fParity = EVENPARITY;
	dcb.ByteSize = 7;
	dcb.StopBits = TWOSTOPBITS;
	// set the correct ASCII Xon/xoff characters
	dcb.XoffChar = 19;
	dcb.XonChar = 17;
	// use Xon/Xoff
	dcb.fOutX = TRUE;
	dcb.fInX = TRUE; 

	dcb.fDtrControl = DTR_CONTROL_ENABLE;
	dcb.fRtsControl = RTS_CONTROL_ENABLE;


	// LED
	//dcb.BaudRate = 9600;
	//dcb.fParity = EVENPARITY;
	//dcb.ByteSize =   7;
	//dcb.StopBits = TWOSTOPBITS;
	//dcb.fDtrControl = DTR_CONTROL_DISABLE;
	//dcb.fOutX = FALSE;
	//dcb.fInX = FALSE; 

	
	// print active settings
	printf("DCBlength : %u\n",dcb.DCBlength);      
	printf("BaudRate : %u\n",dcb.BaudRate);       
	printf("fBinary : %u\n",dcb.fBinary);     
	printf("fParity : %u\n",dcb.fParity);     
	printf("fOutxCtsFlow : %u\n",dcb.fOutxCtsFlow); 
	printf("fOutxDsrFlow : %u\n",dcb.fOutxDsrFlow); 
	printf("fDtrControl : %u\n",dcb.fDtrControl);  
	printf("fDsrSensitivity : %u\n",dcb.fDsrSensitivity); 
	printf("fTXContinueOnXoff : %u\n",dcb.fTXContinueOnXoff); 
	printf("fOutX : %u\n",dcb.fOutX);       
	printf("fInX : %u\n",dcb.fInX);        
	printf("fErrorChar : %u\n",dcb.fErrorChar);  
	printf("fNull : %u\n",dcb.fNull);       
	printf("fRtsControl : %u\n",dcb.fRtsControl);  
	printf("fAbortOnError : %u\n",dcb.fAbortOnError); 
	printf("fDummy2 : %u\n",dcb.fDummy2);     
	printf("wReserved : %u\n",dcb.wReserved);       
	printf("XonLim : %u\n",dcb.XonLim);          
	printf("XoffLim : %u\n",dcb.XoffLim);         
	printf("ByteSize : %u\n",dcb.ByteSize);        
	printf("Parity : %u\n",dcb.Parity);          
	printf("StopBits : %u\n",dcb.StopBits);        
	printf("XonChar : %u\n",dcb.XonChar);         
	printf("XoffChar : %u\n",dcb.XoffChar);        
	printf("ErrorChar : %u\n",dcb.ErrorChar);       
	printf("EofChar : %u\n",dcb.EofChar);         
	printf("EvtChar : %u\n",dcb.EvtChar);         
	printf("wReserved1 : %u\n",dcb.wReserved1);   
	

	// Set the altered Com port settings
	if (!SetCommState(hComm, &dcb))  throw WinException("SerialConnection::connect(): SetCommState()", GetLastError());


	// Set the timeout settings for the port
	// The timeouts define when a read operation is completed and ReadFile() returns
	COMMTIMEOUTS commtimeouts = {0};
	if (!GetCommTimeouts(hComm, &commtimeouts)) throw WinException("SerialConnection::connect(): GetCommTimeouts()", GetLastError());

	
	/*
	If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called:
	If there are any bytes in the input buffer, ReadFile returns immediately with the bytes in the buffer.
	If there are no bytes in the input buffer, ReadFile waits until a byte arrives and then returns immediately.
	If no bytes arrive within the time specified by ReadTotalTimeoutConstant, ReadFile times out.
	*/

	commtimeouts.WriteTotalTimeoutConstant = 0;
	commtimeouts.WriteTotalTimeoutMultiplier = 0;

	commtimeouts.ReadIntervalTimeout = MAXDWORD;
	commtimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
	commtimeouts.ReadTotalTimeoutConstant = MAXDWORD - 1; // return only when data was received (technically after MAXDWORD-1 ms)

	if (!SetCommTimeouts(hComm, &commtimeouts)) throw WinException("SerialConnection::connect(): SetCommTimeouts()", GetLastError());

	printf("Opened port: %s\n",comport.c_str());
}





 // throws WinException
 vector<string> SerialConnection::getPorts() {

	 vector<string> result;

	 char targetPath[65536];

	 DWORD targetPathLength = ::QueryDosDeviceA( NULL, targetPath, sizeof(targetPath));
	 if (targetPathLength == 0) throw WinException("SerialConnection::getPorts() : QueryDosDeviceA()", GetLastError());

	 DWORD pos = 0;
	 DWORD start = 0;
	 
	 while (pos < targetPathLength) {
		 // print all the 0-delimited strings inside targetPath
		 if (targetPath[pos] == 0) {
			 
			 // if the devicename starts with "COM", add it to the result set
			 if (strncmp(targetPath + start, "COM", 3) == 0) result.push_back( string(targetPath + start));
			 start = pos + 1;
		 }
		 ++pos;
	 }
	 return result;
 }


 


 

